const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const { Low } = require('lowdb');
const { JSONFile } = require('lowdb/node');
const path = require('path');
const axios = require('axios');
const cors = require('cors');

const app = express();

// Enable CORS for all routes
app.use(cors({
    origin: '*',
    methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
    allowedHeaders: ['Content-Type', 'Authorization']
}));

app.use(express.json());

// Database setup
const dbPath = path.join(process.cwd(), 'db.json');
const adapter = new JSONFile(dbPath);
const db = new Low(adapter, { models: [] });

// Initialize database
async function initializeDatabase() {
    await db.read();
    await db.write();
}

// Unified API Endpoint
app.post('/v1/chat/completions', async (req, res) => {
    try {
        await db.read();
        
        // Validate request body
        if (!req.body.model) {
            return res.status(400).json({
                error: {
                    message: 'Missing required parameter: model',
                    type: 'invalid_request_error',
                    param: 'model',
                    code: 'missing_parameter'
                }
            });
        }
        
        if (!req.body.messages || !Array.isArray(req.body.messages) || req.body.messages.length === 0) {
            return res.status(400).json({
                error: {
                    message: 'Missing or empty required parameter: messages',
                    type: 'invalid_request_error',
                    param: 'messages',
                    code: 'invalid_parameter'
                }
            });
        }
        
        const modelName = req.body.model;
        const model = db.data.models.find(m => m.name === modelName);

        if (!model) {
            return res.status(404).json({ 
                error: {
                    message: `The model '${modelName}' does not exist`,
                    type: 'invalid_request_error',
                    param: 'model',
                    code: 'model_not_found'
                }
            });
        }

        // Prepare headers
        const headers = {
            'Content-Type': 'application/json'
        };
        
        if (model.apiKey) {
            headers['Authorization'] = `Bearer ${model.apiKey}`;
        }

        // Forward request to the target model
        const targetUrl = `${model.baseUrl}/v1/chat/completions`;
        
        // Check if streaming is requested
        if (req.body.stream === true) {
            // Set SSE headers for streaming
            res.setHeader('Content-Type', 'text/event-stream');
            res.setHeader('Cache-Control', 'no-cache');
            res.setHeader('Connection', 'keep-alive');
            res.setHeader('Access-Control-Allow-Origin', '*');
            res.setHeader('Access-Control-Allow-Headers', 'Cache-Control');
            
            try {
                const response = await axios.post(targetUrl, req.body, {
                    headers,
                    responseType: 'stream'
                });
                
                // Pipe the streaming response
                response.data.on('data', (chunk) => {
                    res.write(chunk);
                });
                
                response.data.on('end', () => {
                    res.end();
                });
                
                response.data.on('error', (error) => {
                    console.error('Streaming error:', error);
                    res.end();
                });
                
            } catch (streamError) {
                console.error('Stream setup error:', streamError);
                res.write('data: {"error": {"message": "Streaming failed", "type": "stream_error"}}\n\n');
                res.end();
            }
        } else {
            // Non-streaming response
            const response = await axios.post(targetUrl, req.body, { headers });
            res.json(response.data);
        }
    } catch (error) {
        // Log more detailed error information for debugging
        console.error('Chat completion error (via /v1/chat/completions):');
        console.error('  Request body:', JSON.stringify(req.body, null, 2));
        console.error('  Error message:', error.message);
        if (error.response) {
            console.error('  Response status:', error.response.status);
            console.error('  Response data:', JSON.stringify(error.response.data, null, 2));
        }
        
        // Handle different types of errors
        if (error.response) {
            // Forward the error from the target API
            res.status(error.response.status).json(error.response.data);
        } else if (error.code === 'ECONNREFUSED' || error.code === 'ENOTFOUND') {
            res.status(503).json({
                error: {
                    message: 'The model service is currently unavailable',
                    type: 'service_unavailable_error',
                    code: 'service_unavailable'
                }
            });
        } else {
            res.status(500).json({ 
                error: {
                    message: 'An unexpected error occurred',
                    type: 'internal_server_error',
                    code: 'internal_error'
                }
            });
        }
    }
});

// Alternative endpoint for some AI chat software that use /v1/responses
app.post('/v1/responses', async (req, res) => {
    try {
        await db.read();
        
        // Validate request body
        if (!req.body.model) {
            return res.status(400).json({
                error: {
                    message: 'Missing required parameter: model',
                    type: 'invalid_request_error',
                    param: 'model',
                    code: 'missing_parameter'
                }
            });
        }
        
        if (!req.body.messages || !Array.isArray(req.body.messages) || req.body.messages.length === 0) {
            return res.status(400).json({
                error: {
                    message: 'Missing or empty required parameter: messages',
                    type: 'invalid_request_error',
                    param: 'messages',
                    code: 'invalid_parameter'
                }
            });
        }
        
        const modelName = req.body.model;
        const model = db.data.models.find(m => m.name === modelName);

        if (!model) {
            return res.status(404).json({ 
                error: {
                    message: `The model '${modelName}' does not exist`,
                    type: 'invalid_request_error',
                    param: 'model',
                    code: 'model_not_found'
                }
            });
        }

        // Prepare headers
        const headers = {
            'Content-Type': 'application/json'
        };
        
        if (model.apiKey) {
            headers['Authorization'] = `Bearer ${model.apiKey}`;
        }

        // Forward request to the target model
        const targetUrl = `${model.baseUrl}/v1/chat/completions`;
        
        // Check if streaming is requested
        if (req.body.stream === true) {
            // Set SSE headers for streaming
            res.setHeader('Content-Type', 'text/event-stream');
            res.setHeader('Cache-Control', 'no-cache');
            res.setHeader('Connection', 'keep-alive');
            res.setHeader('Access-Control-Allow-Origin', '*');
            res.setHeader('Access-Control-Allow-Headers', 'Cache-Control');
            
            try {
                const response = await axios.post(targetUrl, req.body, {
                    headers,
                    responseType: 'stream'
                });
                
                // Pipe the streaming response
                response.data.on('data', (chunk) => {
                    res.write(chunk);
                });
                
                response.data.on('end', () => {
                    res.end();
                });
                
                response.data.on('error', (error) => {
                    console.error('Streaming error:', error);
                    res.end();
                });
                
            } catch (streamError) {
                console.error('Stream setup error:', streamError);
                res.write('data: {"error": {"message": "Streaming failed", "type": "stream_error"}}\n\n');
                res.end();
            }
        } else {
            // Non-streaming response
            const response = await axios.post(targetUrl, req.body, { headers });
            res.json(response.data);
        }
    } catch (error) {
        // Log more detailed error information for debugging
        console.error('Chat completion error (via /v1/responses):');
        console.error('  Request body:', JSON.stringify(req.body, null, 2));
        console.error('  Error message:', error.message);
        if (error.response) {
            console.error('  Response status:', error.response.status);
            console.error('  Response data:', JSON.stringify(error.response.data, null, 2));
        }
        
        // Handle different types of errors
        if (error.response) {
            // Forward the error from the target API
            res.status(error.response.status).json(error.response.data);
        } else if (error.code === 'ECONNREFUSED' || error.code === 'ENOTFOUND') {
            res.status(503).json({
                error: {
                    message: 'The model service is currently unavailable',
                    type: 'service_unavailable_error',
                    code: 'service_unavailable'
                }
            });
        } else {
            res.status(500).json({ 
                error: {
                    message: 'An unexpected error occurred',
                    type: 'internal_server_error',
                    code: 'internal_error'
                }
            });
        }
    }
});

// OpenAI Compatible Models API
app.get('/v1/models', async (req, res) => {
    try {
        await db.read();
        const models = db.data.models.map(model => ({
            id: model.name,
            object: 'model',
            created: Math.floor(Date.now() / 1000),
            owned_by: model.type || 'user',
            permission: [{
                id: `modelperm-${model.id}`,
                object: 'model_permission',
                created: Math.floor(Date.now() / 1000),
                allow_create_engine: false,
                allow_sampling: true,
                allow_logprobs: true,
                allow_search_indices: false,
                allow_view: true,
                allow_fine_tuning: false,
                organization: '*',
                group: null,
                is_blocking: false
            }],
            root: model.name,
            parent: null
        }));
        
        res.json({
            object: 'list',
            data: models
        });
    } catch (error) {
        console.error('Models list error:', error.message);
        res.status(500).json({ 
            error: 'Internal server error', 
            message: error.message 
        });
    }
});

// Management API
app.get('/api/models', async (req, res) => {
    await db.read();
    res.json(db.data.models);
});

app.post('/api/models', async (req, res) => {
    const newModel = { id: Date.now().toString(), ...req.body };
    db.data.models.push(newModel);
    await db.write();
    res.status(201).json(newModel);
});

app.put('/api/models/:id', async (req, res) => {
    const modelIndex = db.data.models.findIndex(m => m.id === req.params.id);
    if (modelIndex === -1) {
        return res.status(404).json({ error: 'Model not found' });
    }
    db.data.models[modelIndex] = { ...db.data.models[modelIndex], ...req.body };
    await db.write();
    res.json(db.data.models[modelIndex]);
});

app.delete('/api/models/:id', async (req, res) => {
    const modelIndex = db.data.models.findIndex(m => m.id === req.params.id);
    if (modelIndex === -1) {
        return res.status(404).json({ error: 'Model not found' });
    }
    db.data.models.splice(modelIndex, 1);
    await db.write();
    res.status(204).send();
});

// Model detection endpoint
app.post('/api/detect-models', async (req, res) => {
    const { baseUrl, type, apiKey } = req.body;
    
    try {
        let models = [];
        
        if (type === 'ollama') {
            // Detect Ollama models
            const response = await axios.get(`${baseUrl}/api/tags`, {
                timeout: 10000
            });
            models = response.data.models?.map(model => model.name) || [];
        } else if (type === 'openai') {
            // Detect OpenAI compatible models
            const headers = {
                'Content-Type': 'application/json'
            };
            if (apiKey) {
                headers['Authorization'] = `Bearer ${apiKey}`;
            }
            
            const response = await axios.get(`${baseUrl}/v1/models`, {
                headers,
                timeout: 10000
            });
            models = response.data.data?.map(model => model.id) || [];
        }
        
        res.json({ models });
    } catch (error) {
        console.error('Model detection error:', error.message);
        res.status(400).json({ 
            message: '无法连接到指定的服务器，请检查URL和网络连接',
            error: error.message 
        });
    }
});

// Model test endpoint
app.post('/api/test-model', async (req, res) => {
    const { id } = req.body;
    
    try {
        await db.read();
        const model = db.data.models.find(m => m.id === id);
        
        if (!model) {
            return res.status(404).json({ error: 'Model not found' });
        }
        
        let testEndpoint;
        let headers = { 'Content-Type': 'application/json' };
        
        if (model.type === 'ollama') {
            testEndpoint = `${model.baseUrl}/api/tags`;
        } else if (model.type === 'openai') {
            testEndpoint = `${model.baseUrl}/v1/models`;
            if (model.apiKey) {
                headers['Authorization'] = `Bearer ${model.apiKey}`;
            }
        }
        
        const response = await axios.get(testEndpoint, {
            headers,
            timeout: 5000
        });
        
        res.json({ status: 'success', message: '连接测试成功' });
    } catch (error) {
        console.error('Model test error:', error.message);
        res.status(400).json({ 
            status: 'error',
            message: '连接测试失败',
            error: error.message 
        });
    }
});

let server;

function start() {
    initializeDatabase().then(() => {
        server = app.listen(3001, () => {
            console.log('API server listening on port 3001');
        });
    });
}

function stop() {
    if (server) {
        server.close();
    }
}

module.exports = { start, stop };

start();